﻿namespace Database
{
    #region usings

    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Linq;

    using Model;

    #endregion

    public sealed class DatabaseManager
    {
        #region Constants and Fields

        private static readonly DatabaseManager instance = new DatabaseManager();

        private readonly object syncObj = new object();

        private DataContext dataContext;

        private string fileName;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Explicit static constructor to tell C# compiler
        /// not to mark type as beforefieldinit
        /// </summary>
        [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", 
            "SA1642:ConstructorSummaryDocumentationMustBeginWithStandardText")]
        [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1409:RemoveUnnecessaryCode")]
        static DatabaseManager()
        {
        }

        private DatabaseManager(string fileName)
        {
            if (fileName == null)
            {
                throw new ArgumentNullException("fileName");
            }

            this.fileName = fileName;
        }

        private DatabaseManager()
        {
            fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "BookOnlineStorage.xml");
        }

        #endregion

        #region Properties

        public static DatabaseManager Instance
        {
            get
            {
                return instance;
            }
        }

        #endregion

        #region Public Methods

        public Book AddBook(Book book)
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(false))
                {
                    context.Books.Add(book);
                    context.SubmitChanges();
                    return book;
                }
            }
        }

        public Location AddLocation(Location location)
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(false))
                {
                    context.Locations.Add(location);
                    context.SubmitChanges();
                    return location;
                }
            }
        }

        public Person AddPerson(Person person)
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(false))
                {
                    context.Persons.Add(person);
                    context.SubmitChanges();
                    return person;
                }
            }
        }

        public Location FindLocationByRoom(string room)
        {
            using (var context = CreateDataContext(true))
            {
                var location =
                    context.Locations.Where(l => l.Room.Equals(room, StringComparison.InvariantCultureIgnoreCase)).
                        SingleOrDefault();
                return location;
            }
        }

        public Person FindPersonByName(string name)
        {
            using (var context = CreateDataContext(true))
            {
                var person =
                    context.Persons.Where(l => l.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).
                        SingleOrDefault();
                return person;
            }
        }

        public List<Book> GetAllBooks()
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(true))
                {
                    return context.Books;
                }
            }
        }

        public bool RemoveBook(Guid id)
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(false))
                {
                    var book = context.Books.Where(b => b.Id == id).SingleOrDefault();
                    if (book == null)
                    {
                        return false;
                    }

                    bool result = context.Books.Remove(book);
                    context.SubmitChanges();
                    return result;
                }
            }
        }

        public bool UpdateBook(Book newBook)
        {
            lock (syncObj)
            {
                using (var context = CreateDataContext(false))
                {
                    var oldBook = context.Books.Where(b => b.Id == newBook.Id).SingleOrDefault();
                    if (oldBook == null)
                    {
                        return false;
                    }

                    oldBook.Update(newBook);
                    context.SubmitChanges();
                    return true;
                }
            }
        }

        #endregion

        #region Methods

        private DataContext CreateDataContext(bool readOnly)
        {
            if (dataContext == null)
            {
                lock (syncObj)
                {
                    if (dataContext == null)
                    {
                        dataContext = LoadData(readOnly);
                    }
                }
            }

            return dataContext;
        }

        private DataContext LoadData(bool readOnly)
        {
            return new DataContext(fileName, readOnly);
        }

        #endregion
    }
}